# UserInterface - Main window of the Entertainer frontend
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import os
import gtk
import time
import clutter
from clutter import cluttergtk

# Utility classes
from frontend.media_player import MediaPlayer
from frontend.gui.widgets.desktop import Desktop
from frontend.gui.screen_history import ScreenHistory
from frontend.gui.frontend_window import FrontendWindow
from frontend.gui.widgets.menu_overlay import MenuOverlay

# Transition effects
from frontend.gui.transitions.transition import Transition
from frontend.gui.transitions.fade_transition import FadeTransition
from frontend.gui.transitions.slide_transition import SlideTransition
from frontend.gui.transitions.no_effect_transition import NoEffectTransition
from frontend.gui.transitions.zoom_and_fade_transition import ZoomAndFadeTransition
from frontend.gui.transitions.slide_and_fade_transition import SlideAndFadeTransition

# Screens
from frontend.gui.screens.screen import Screen
from frontend.gui.screens.main_screen import MainScreen

# Dialogs
from frontend.gui.screens.question_dialog import QuestionDialog

# RSS Screens
from frontend.gui.screens.rss_screen import RssScreen
from frontend.gui.screens.feed_screen import FeedScreen
from frontend.gui.screens.feed_entry_screen import FeedEntryScreen

# Photograph library
from frontend.gui.screens.photo_screen import PhotoScreen
from frontend.gui.screens.photographs_screen import PhotographsScreen
from frontend.gui.screens.photoalbums_screen import PhotoAlbumsScreen

# Music library
from frontend.gui.screens.music_screen import MusicScreen
from frontend.gui.screens.artist_screen import ArtistScreen
from frontend.gui.screens.album_screen import AlbumScreen
from frontend.gui.screens.audio_play_screen import AudioPlayScreen

# Video library
from frontend.gui.screens.video_screen import VideoScreen
from frontend.gui.screens.movie_screen import MovieScreen
from frontend.gui.screens.tv_serie_screen import TVSerieScreen
from frontend.gui.screens.tv_serie_episodes_screen import TVSerieEpisodesScreen

# Television
from frontend.gui.screens.tv_osd_screen import TvOSDScreen

# Weather
from frontend.gui.screens.weather_screen import WeatherScreen

# OSD Screens
from frontend.gui.screens.video_osd_screen import VideoOSDScreen

class UserInterface:
    """
    Entertainer frontend user interface.

    Object from this class represents the main window of the Entertainer client.
    This window is actually a wrapper for Pigment viewport.
    """
    
    # Size of the stage (Screens should use these coordinates)
    # See also methods: self.get_abs_x_pos and self.get_abs_y_pos
    STAGE_WIDTH = 1366
    STAGE_HEIGHT = 768
    #STAGE_WIDTH = 1920
    #STAGE_HEIGHT = 1080
    
    def __init__(self, frontend):
        """
        Initialize frontend GUI.
        @param frontend: FrontendClient object
        """
        self.frontend = frontend
        self.theme = frontend.getTheme()
        self.logger = frontend.get_logger()
        
        # Create stage
        self.stage = clutter.stage_get_default()
        self.stage.connect('key-press-event', self.handle_key_press_event)
        self.stage.set_color(self.theme.getColor("background"))
        self.stage.set_size(self.__class__.STAGE_WIDTH, self.__class__.STAGE_HEIGHT)
        self.stage.set_title("Entertainer")
        
        #self.desktop = Desktop()
        #self.desktop.set_position(0,0)
        #self.desktop.show()
        #self.stage.add(self.desktop)
        
        # Initialize Screen history (allows user to navigate "back")
        self.history = ScreenHistory(self.stage, 
                                     frontend.get_configuration().history_size()) 
        
        # Intialize media player
        self.player = MediaPlayer(self.stage, 
                                  self.__class__.STAGE_WIDTH, 
                                  self.__class__.STAGE_HEIGHT)
        
        # Initialize menu overlay texture
        self.overlay_status = False
        self.menu_overlay = MenuOverlay(self.theme)
        self.menu_overlay.set_opacity(0)
        self.menu_overlay.set_name("overlay")
        self.menu_overlay.show()
        self.menu_overlay.set_size(self.__class__.STAGE_WIDTH, self.__class__.STAGE_HEIGHT)
        self.stage.add(self.menu_overlay)
        
        # Transition object. Handels effects between screen changes.
        if self.frontend.get_configuration().show_effects():
            self.transition = self.createTransition(self.frontend.get_configuration().transition_effect())
        else:
            self.transition = self.createTransition("No effect")
        
        # Embed Stage into GTK-window
        self.window = FrontendWindow(self.frontend)
        gtkwidget = cluttergtk.Embed()
        self.window.setClutterWidget(gtkwidget)
        #self.resizeStage(800,450)
        self.resizeStage(1366,768)

        self.logger.debug("Frontend GUI initialized succesfully")
    
    def start_up(self, fullscreen):
        """
        This is called once when Entertainer is starting up.
        @param fullscreen: Should we start in fullscreen mode
        """
        self.show()
        self.current = self.createScreen("main")
        self.transition.forwardEffect(None, self.current)
        self.enable_menu_overlay()
    
    def shutdown(self):
        """
        This is called once when Entertainer is shuting down.
        """
        self.frontend.quit_frontend()
        
    def get_abs_x_pos(self, percent):
        """
        Transform relative position to absolute position of the screen.
        @param percent: float value between 0-1
        @return absolute X coordinate
        """
        return int(float(self.__class__.STAGE_WIDTH) * percent)
    
    def get_abs_y_pos(self, percent):
        """
        Transform relative position to absolute position of the screen.
        @param percent: float value between 0-1
        @return absolute Y coordinate
        """
        return int(float(self.__class__.STAGE_HEIGHT) * percent)

    def getStage(self):
        """
        Get user interface stage.
        @return - Stage object (of Clutter)
        """
        return self.stage
    
    def get_media_player(self):
        """
        Get media player object
        @return: MediaPlayer object
        """
        return self.player
    
    def set_fullscreen(self, boolean):
        """
        Set UI in fullscreen mode.
        """
        if boolean:
            self.stage.fullscreen()
        else:
            self.stage.unfullscreen()
    
    def resizeStage(self, width, height):
        """
        Resize and scale stage.
        @param width: New width for the stage
        @param height: New height for the stage
        """
        self.stage.set_scale(float(width) / float(self.__class__.STAGE_WIDTH), 
                               float(height) / float(self.__class__.STAGE_HEIGHT))
        self.stage.set_size(width, height)
    
    def createScreen(self, type, data=None):
        """
        Factory method for screens. Create a Screen object of given type.
        
        @param type: Type of the screen as a string.
        @param data: Data can contain some screen spesific data
        """
        if type == "main":
            return MainScreen(self, self.frontend)
        elif type == "rss":
            return RssScreen(self, self.frontend, self.frontend.getTheme())
        elif type == "feed":
            return FeedScreen(self, self.frontend, data)
        elif type == "entry":
            return FeedEntryScreen(self, self.frontend, data)
        elif type == "video_osd":
            return VideoOSDScreen(self, self.frontend.getTheme(), self.player)
        elif type == "tv_osd":
            return TvOSDScreen(self, self.frontend.getTheme(), self.player)
        elif type == "photo_albums":
            return PhotoAlbumsScreen(self, self.frontend)
        elif type == "photographs":
            return PhotographsScreen(self, self.frontend, data)
        elif type == "photo":
            return PhotoScreen(self, self.frontend, data)
        elif type == "music":
            return MusicScreen(self, self.frontend)
        elif type == "artist":
            return ArtistScreen(self, self.frontend, data)
        elif type == "music_album":
            return AlbumScreen(self, self.frontend, data)
        elif type == "audio_play":
            return AudioPlayScreen(self, self.frontend, data)
        elif type == "videos":
            return VideoScreen(self, self.frontend)
        elif type == "movie":
            return MovieScreen(self, self.frontend, data)
        elif type == "tv-serie":
            return TVSerieScreen(self, self.frontend, data)
        elif type =="tv-episodes":
            return TVSerieEpisodesScreen(self, self.frontend, data)
        elif type == "weather":
            return WeatherScreen(self, self.frontend)
        elif type == "question_dialog":
            return QuestionDialog(self, self.frontend, data)
    
    def createTransition(self, type):
        """
        Factory method for transitions. Create a Transition object of given type.
        @param type: Transition type as a string value.
        """
        if type == "No effect":
            return NoEffectTransition(self.stage)
        elif type == "Crossfade":
            return FadeTransition(self.stage)
        elif type =="Zoom and fade":
            return ZoomAndFadeTransition(self.stage)
        elif type =="Slide and fade":
            return SlideAndFadeTransition(self.stage)
        elif type == "Slide":
            return SlideTransition(self.stage)
        
    def show(self):
        """
        Show user interface.
        """
        self.stage.show()
        self.window.show()
        
    def enable_menu_overlay(self):
        """
        Enable menu overlay. Overlay should be enabled always when there is
        a video playing and menu showing at the same time. Overlay is not part
        of any specific screen. It is used for all screens when neccesary.
        """
        self.overlay_status = True
        if self.frontend.get_configuration().show_effects():
            self.menu_overlay.fade_in()
        else:
            self.menu_overlay.set_opacity(255)
        
    def disable_menu_overlay(self):
        """
        Disable menu overlay. Overlay should be disabled when current screen is
        a type of Screen.OSD.
        """
        self.overlay_status = False
        if self.frontend.get_configuration().show_effects():
            self.menu_overlay.fade_out()
        else:
            self.menu_overlay.set_opacity(0)
             
    def is_overlay(self):
        """
        Get menu overlay status.
        @return boolean value. True if menu overlay is active, otherwise False.
        """
        return self.overlay_status
    
    def changeScreen(self, screen, direction):
        """
        Execute screen change.
        @param screen - Screen object. This screen is displayed after change.
        @param direction - Should we use forward or backward effect
        """
        # Enable/Disable menu overlay
        if screen.get_type() == Screen.OSD and self.is_overlay():
            self.disable_menu_overlay()
        elif screen.get_type() != Screen.OSD and not self.is_overlay():
            self.enable_menu_overlay()

        # Add current screen to screen history
        if direction == Transition.FORWARD and self.current.get_type() != Screen.DIALOG:
            self.history.add_screen(self.current)
            
        # Change screen (Logical). Graphics is changed via animation
        from_screen = self.current
        self.current = screen
        
        # Animate screen change
        if direction == Transition.FORWARD:
            self.transition.forwardEffect(from_screen, screen)
        elif direction == Transition.BACKWARD:
            self.transition.backwardEffect(from_screen, screen)
        
    def handle_key_press_event(self, stage, event):
        """
        User pressed a key.
        
        This object handels only the "GLOBAL" keys like fullscreen toggle. All
        other key pressings are notified to current Screen object. Screens can
        decide how to react on key events.
        @param stage: Stage object which received key pressing event
        @param event: Event object
        """
        # 'Backspace' pressed (Go to previous screen) (DO NOTHING IF DIALOG IS ACTIVE)
        if event.keyval == clutter.keysyms.BackSpace and self.current.get_type() != Screen.DIALOG:
            if not self.history.is_empty():
                screen = self.history.get_screen()
                screen.update()
                self.changeScreen(screen, Transition.BACKWARD)
        
        # 'H' pressed Go to Home screen (Main menu)
        elif event.keyval == clutter.keysyms.h:
            self.changeScreen(self.createScreen("main"), Transition.FORWARD)
            
        # 'F' pressed (Toggle fullscreen mode)
        elif event.keyval == clutter.keysyms.f:
            self.window.toggle_fullscreen(None)
        
        # 'P' pressed (Toggle play/pause) and forward event to current Screen
        elif event.keyval == clutter.keysyms.p:
            if self.player.is_playing():
                self.player.pause()
                self.current.handle_key_press_event(event)
            else:
                self.player.play()
                self.current.handle_key_press_event(event)
        
         # 'S' pressed (Stop media player) and forward event to current Screen
        elif event.keyval == clutter.keysyms.s:
            if self.player.is_playing():
                self.player.stop()
                self.current.handle_key_press_event(event)
        
        # Change video playback aspect ratio    
        elif event.keyval == clutter.keysyms._1:
            self.player.set_native_ratio()
            self.current.handle_key_press_event(event)
        elif event.keyval == clutter.keysyms._2:
            self.player.set_widescreen_ratio()
            self.current.handle_key_press_event(event)
        elif event.keyval == clutter.keysyms._3:
            self.player.set_zoom_ratio()
            self.current.handle_key_press_event(event)
        elif event.keyval == clutter.keysyms._4:
            self.player.set_intelligent_ratio()
            self.current.handle_key_press_event(event)
                
        # Skip video
        elif event.keyval == clutter.keysyms.x:
            self.player.skip_backward()
        elif event.keyval == clutter.keysyms.c:
            self.player.skip_forward()
            
        # 'Q' or Escape pressed (Quit frontend)
        elif event.keyval == clutter.keysyms.q or event.keyval == clutter.keysyms.Escape:
            self.shutdown()
        
        # Non-global key pressed. Notify current screen and let it handle event
        else:
            self.current.handle_key_press_event(event)
